#include "jam.h"
#include "hash.h"
#include "lists.h"
#include "newstr.h"
#include "pathsys.h"
#include "pathsplit.h"
#include "timestamp.h"
#include "search.h"

static LIST *searchdirs = 0;

typedef struct _search SEARCH;
struct _search {
  const char *key;
  const char *path;
  time_t time;
};
struct hash *searchhash = 0;

const char *search( const char *source, const char *_header, time_t *time ) {
  PATHNAME f[1];
  char buf[MAXJPATH];
  char buf2[MAXSYM], *header = buf2;
  char buf3[MAXJPATH];
  int system = ( _header[0] == '<' );
  LIST *list = searchdirs->next;
  int dMode = 0;
  int fnlen = strlen( source );
  if( source[fnlen - 2] == '.' && source[fnlen - 1] == 'd' ) {
    dMode = 1;
  }
  strcpy( header, _header + 1 );
  header[strlen( header ) - 1] = '\0';
  path_parse( source, f );
  path_parent( f );
  path_build( f, buf3, 1 );
  if( DEBUG_SEARCH ) {
    printf( "search %s\n  included by %s\n", _header, source );
  }
  {
    char key[MAXJPATH] = "";
    SEARCH search, *s = &search;
    if( !system ) {
      strcpy( key, buf3 );
      strcat( key, "," );
    }
    strcat( key, _header );
    s->key = key;
    if( !searchhash ) {
      searchhash = hashinit( sizeof( SEARCH ), "search" );
    }
    if( hashcheck( searchhash, ( HASHDATA ** )&s ) ) {
      if( DEBUG_SEARCH ) {
        printf( "  %s: %s [CACHED]\n", _header, s->time ? s->path : "*missing*" );
      }
      *time = s->time;
      return s->path;
    }
  }
  if( !system ) {
    searchdirs->string = buf3;
    list = searchdirs;
  }
  path_parse( header, f );
  f->f_grist.ptr = 0;
  f->f_grist.len = 0;
  for( ; list; list = list->next ) {
    f->f_root.ptr = list->string;
    f->f_root.len = strlen( list->string );
    path_build( f, buf, 1 );
    {
      PATHSPLIT f;
      char buf2[MAXJPATH];
      path_split( buf, &f );
      path_normalize( &f, NULL );
      path_tostring( &f, buf2 );
      strcpy( buf, buf2 );
    }
    if( DEBUG_SEARCH ) {
      printf( "  %s: %s [TRY]\n", _header, buf );
    }
    timestamp( buf, time );
    if( *time ) {
      char key[MAXJPATH] = "";
      SEARCH search, *s = &search;
      if( !system ) {
        strcpy( key, buf3 );
        strcat( key, "," );
      }
      strcat( key, _header );
      s->key = newstr( key );
      s->time = *time;
      s->path = newstr( buf );
      ( void ) hashenter( searchhash, ( HASHDATA ** )&s );
    }
    if( *time ) {
      return newstr( buf );
    }
  }
  if( !dMode ) {
    {
      char key[MAXJPATH] = "";
      SEARCH search, *s = &search;
      if( !system ) {
        strcpy( key, buf3 );
        strcat( key, "," );
      }
      strcat( key, _header );
      s->key = newstr( key );
      s->time = 0;
      s->path = NULL;
      ( void ) hashenter( searchhash, ( HASHDATA ** )&s );
    }
    *time = 0;
    return NULL;
  } else {
    f->f_root.ptr = 0;
    f->f_root.len = 0;
    path_build( f, buf, 1 );
    {
      PATHSPLIT f;
      char buf2[MAXJPATH];
      path_split( buf, &f );
      path_normalize( &f, NULL );
      path_tostring( &f, buf2 );
      strcpy( buf, buf2 );
    }
    if( DEBUG_SEARCH ) {
      printf( "search %s: %s\n", _header, buf );
    }
    timestamp( buf, time );
    if( *time ) {
      char key[MAXJPATH] = "";
      SEARCH search, *s = &search;
      if( !system ) {
        strcpy( key, buf3 );
        strcat( key, "," );
      }
      strcat( key, _header );
      s->key = newstr( key );
      s->time = *time;
      s->path = newstr( buf );
      ( void ) hashenter( searchhash, ( HASHDATA ** )&s );
    }
    if( *time ) {
      return newstr( buf );
    }
    {
      char key[MAXJPATH] = "";
      SEARCH search, *s = &search;
      if( !system ) {
        strcpy( key, buf3 );
        strcat( key, "," );
      }
      strcat( key, _header );
      s->key = newstr( key );
      s->time = 0;
      s->path = NULL;
      ( void ) hashenter( searchhash, ( HASHDATA ** )&s );
    }
    *time = 0;
    return NULL;
  }
}

void search_adddir( const char *path ) {
  PATHSPLIT f;
  char buf[MAXJPATH];
  path_split( path, &f );
  path_normalize( &f, NULL );
  path_tostring( &f, buf );
  searchdirs = list_new( searchdirs, buf, 0 );
  hashdone( searchhash );
  searchhash = 0;
}

void search_init( void ) {
  donesearch();
  searchdirs = list_new( searchdirs, "SEARCH[0]", 0 );
}

void donesearch( void ) {
  searchdirs = 0;
  hashdone( searchhash );
  searchhash = 0;
}
